package com.test.test01.schedule.runnable;

import cn.hutool.core.util.ObjectUtil;
import com.test.test01.common.base.error.CommonException;
import com.test.test01.schedule.register.CronTaskRegister;
import com.test.test01.utils.SpringContextUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * Description: 定时任务线程执行对象
 * Create DateTime: 2021/1/4 下午5:37
 *
 * @author houyi
 */
@Slf4j
@Data
public class SchedulingRunnable implements Runnable {

    private Long jobId;

    private String beanName;

    private String methodName;

    private String params;

    private Integer jobCount;

    private String cronExpression;

    private CronTaskRegister cronTaskRegister;

    private JdbcTemplate jdbcTemplate;

    private TransactionTemplate transactionTemplate;

    public SchedulingRunnable(String beanName, String methodName) {
        this.beanName = beanName;
        this.methodName = methodName;
    }

    public SchedulingRunnable(String beanName, String methodName, String params) {
        this.beanName = beanName;
        this.methodName = methodName;
        this.params = params;
    }

    public SchedulingRunnable(Long jobId, String beanName, String methodName, String params, Integer jobCount, String cronExpression,
                              CronTaskRegister cronTaskRegister, JdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) {
        this.jobId = jobId;
        this.beanName = beanName;
        this.methodName = methodName;
        this.params = params;
        this.jobCount = jobCount;
        this.cronExpression = cronExpression;
        this.cronTaskRegister = cronTaskRegister;
        this.jdbcTemplate = jdbcTemplate;
        this.transactionTemplate = transactionTemplate;
    }

    /*
    * Scheduled task是Spring 内置的一个定时器,执行方法为实例化一个Runnable方法
    * 或者实现Runnable接口
    * */
    @Override
    public void run() {
        //log.info("定时任务开始执行 - bean：{}，方法：{}，参数：{}", beanName, methodName, params);
        //long startTime = System.currentTimeMillis();

        log.info("jobCount:{}",jobCount);
        log.info("beanName:{},methodName:{}",beanName,methodName);
        //以事务方式运行
        if (ObjectUtil.equal(1,jobCount)) {
            transactionTemplate.executeWithoutResult(transactionStatus -> {
                try {
                    execute(beanName, methodName, params);

                    jdbcTemplate.update("update system_job set job_status = 2, is_finish = 1 where id = ?", jobId);

                    cronTaskRegister.removeCronTask(new SchedulingRunnable(beanName, methodName, params));
                } catch (Exception e) {
                    SchedulingRunnable task = new SchedulingRunnable(jobId, beanName,
                            methodName, params, jobCount, cronExpression, cronTaskRegister, jdbcTemplate, transactionTemplate);

                    cronTaskRegister.addCronTask(task, cronExpression);

                    throw new RuntimeException(e);
                }
            });
        } else {
            execute(beanName, methodName, params);
        }

        //long times = System.currentTimeMillis() - startTime;
        //log.info("定时任务执行结束 - bean：{}，方法：{}，参数：{}，耗时：{} 毫秒", beanName, methodName, params, times);
    }

    public static void execute(String beanName, String methodName, String params) {
        try {
            Object target = SpringContextUtils.getBean(beanName);

            Method method;
            if (StringUtils.isNotEmpty(params)) {
                //String.class 参数数据目前以逗号拼接 后续可以优化
                method = target.getClass().getDeclaredMethod(methodName, String.class);
            } else {
                method = target.getClass().getDeclaredMethod(methodName);
            }

            ReflectionUtils.makeAccessible(method);
            if (StringUtils.isNotEmpty(params)) {
                method.invoke(target, params);
            } else {
                method.invoke(target);
            }
        } catch (Exception ex) {
            log.error(String.format("任务执行异常 - bean：%s，方法：%s，参数：%s ", beanName, methodName, params), ex);
            throw new CommonException(ex.getMessage());
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
       SchedulingRunnable that = (SchedulingRunnable) o;
        if (params == null) {
            return beanName.equals(that.beanName) &&
                    methodName.equals(that.methodName) &&
                    that.params == null;
        }

        return beanName.equals(that.beanName) &&
                methodName.equals(that.methodName) &&
                params.equals(that.params);
    }

    @Override
    public int hashCode() {
        if (params == null) {
            return Objects.hash(beanName, methodName);
        }

        return Objects.hash(beanName, methodName, params);
    }
}
